package org.grayrabbit.cms.plugin.loaders.classloading;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ScanFile {
	 private static Logger log = LoggerFactory.getLogger(ScanFile.class);
	 private static final FileFilter fileFilter = new JarFileFilter();
	 private File libDir;
	 private Map<String,DeploymentUnit> scannedDeploymentUnits = new HashMap<String,DeploymentUnit>();
	public ScanFile(File libDir) {
		this.libDir = libDir;
	}
	
	public Collection<DeploymentUnit> scan()
	  {
	    List<File> removedFiles = new ArrayList<File>();
	    for ( DeploymentUnit unit : scannedDeploymentUnits.values())
	    {
	      if ((!unit.getPath().exists()) || (!unit.getPath().canRead()))
	      {
	        removedFiles.add(unit.getPath());
	      }
	    }
	    clear(removedFiles);

	    Collection<DeploymentUnit>result = new ArrayList<DeploymentUnit>();
	    File[] files = this.libDir.listFiles(fileFilter);
	    if (files == null)
	    {
	      log.error("listFiles returned null for directory {}",this.libDir.getAbsolutePath());
	    }
	    else
	    {
	      for ( File file : files)
	      {
	        try
	        {
	          if ((isScanned(file)) && (isModified(file)))
	          {
	            clear(file);
	            DeploymentUnit unit = createAndStoreDeploymentUnit(file);
	            if (unit != null)
	              result.add(unit);
	          }
	          else if (!isScanned(file))
	          {
	            DeploymentUnit unit = createAndStoreDeploymentUnit(file);
	            if (unit != null)
	              result.add(unit);
	          }
	        }
	        catch (Exception e)
	        {
	          log.error("Error deploying plugin " + file.getAbsolutePath(), e);
	        }
	      }
	    }
	    return result;
	  }
	
	public Collection<DeploymentUnit> getDeploymentUnits(){
		return Collections.unmodifiableCollection(this.scannedDeploymentUnits.values());
	}

	  private DeploymentUnit createAndStoreDeploymentUnit(File file) {
		  if (isScanned(file)) {
		      return null;
		    }
		    DeploymentUnit unit = new DeploymentUnit(file);
		    this.scannedDeploymentUnits.put(file.getAbsolutePath(), unit);
		    return unit;
	}

	private void clear(File file) {
		this.scannedDeploymentUnits.remove(file.getAbsolutePath());
	}

	private boolean isScanned(File file) {
		 return locateDeploymentUnit(file) != null;
	}

	private boolean isModified(File file)
	  {
	    DeploymentUnit unit = locateDeploymentUnit(file);
	    return file.lastModified() > unit.lastModified();
	  }

	  private DeploymentUnit locateDeploymentUnit(File file) {
		return this.scannedDeploymentUnits.get(file.getAbsolutePath());
	}

	private void clear(List<File> toUndeploy)
	  {
	    for (File file : toUndeploy)
	    {
	      clear(file);
	    }
	  }
	  
	  static class JarFileFilter implements FileFilter
	  {
	    public boolean accept(File file)
	    {
	      return file.getName().endsWith(".jar");
	    }
	  }
}
